﻿using System;
using System.Net;
using Platformer_Example.Controller.Input;
using Platformer_Example.Controller.Network.Messages;
using Transformable_Engine_v2.Engine.Global;
using Transformable_Engine_v2.Engine.Network;
using Transformable_Engine_v2.Engine.Network.Interfaces;
using Transformable_Engine_v2.Engine.Network.Object;

namespace Platformer_Example.Controller.Network
{
    public class NetworkServer : BaseNetworkController
    {
        public const int Port = 23232;

        public NetworkServer()
        {
            Log.LogInfo("Server started and listening on port " + Port);
            NetworkController = new UdpNetworkClient(BaseNetworkClient.ClientType.Async);
            NetworkController.Receive(IPAddress.Any, Port);
            NetworkController.OnReceive += OnReceive;
        }

        private void OnReceive(IPEndPoint senderInfo, byte[] messageReceived)
        {
            var message = Serialization.Deserialize<NetworkSerializableObject>(messageReceived);
            switch (message.Id)
            {
                case JoinGame.ID:
                    OnJoinGameMessage(Serialization.Deserialize<JoinGame>(message.SerializableObject),
                                      senderInfo.Address);
                    break;
                case SearchGame.ID:
                    OnSearchGameMessage(Serialization.Deserialize<SearchGame>(message.SerializableObject),
                                        senderInfo.Address);
                    break;
                case LoadingDone.ID:
                    OnLoadingDoneMessage(Serialization.Deserialize<LoadingDone>(message.SerializableObject),
                                         senderInfo.Address);
                    break;
                case InputPressed.ID:
                    OnInputPressedMessage(Serialization.Deserialize<InputPressed>(message.SerializableObject),
                                          senderInfo.Address);
                    break;
                case InputReleased.ID:
                    OnInputReleasedMessage(Serialization.Deserialize<InputReleased>(message.SerializableObject),
                                          senderInfo.Address);
                    break;
            }
        }

        public event OnMessageReceivedHandler<InputReleased> OnInputReleased; 

        private void OnInputReleasedMessage(InputReleased message, IPAddress address)
        {
            Log.LogInfo("Received key " + message.Key + " pressed from player " + message.Id);
            if (OnInputReleased != null)
                OnInputReleased(message, address);
        }

        public event OnMessageReceivedHandler<InputPressed> OnInputPressed; 

        private void OnInputPressedMessage(InputPressed message, IPAddress address)
        {
            Log.LogInfo("Received key " + message.Key + " pressed from player " + message.Id);
            if (OnInputPressed != null)
                OnInputPressed(message, address);
        }

        public event OnMessageReceivedHandler<LoadingDone> OnLoadingDone;

        private void OnLoadingDoneMessage(LoadingDone message, IPAddress sender)
        {
            Log.LogInfo("A player has done loading");
            if (OnLoadingDone != null)
                OnLoadingDone(message, sender);
        }

        public event OnMessageReceivedHandler<SearchGame> OnSearchGame;

        private void OnSearchGameMessage(SearchGame message, IPAddress sender)
        {
            Log.LogInfo("A player wants to know if there are open games");
            if (OnSearchGame != null)
                OnSearchGame(message, sender);
        }

        public void SendGameDescription(SearchGameResponse gameResponse, IPAddress sender)
        {
            Log.LogInfo("A client is searching for games. Replying with server's data");
            NetworkController.Send(sender, NetworkClient.Port, gameResponse);
        }

        public event OnMessageReceivedHandler<JoinGame> OnJoinGame;

        private void OnJoinGameMessage(JoinGame message, IPAddress sender)
        {
            Log.LogInfo(message.PlayerName + " is trying to join the game");
            if (OnJoinGame != null)
                OnJoinGame(message, sender);
        }

        public void SendJoinGameDeclineResponse(IPAddress senderAddress)
        {
            Log.LogInfo("Block the player trying to connect to the server");
            NetworkController.Send(senderAddress, NetworkClient.Port, new JoinGameDeclineResponse());
        }

        public void SendJoinGameAcceptResponse(IPAddress[] recipients, JoinGameAcceptResponse message)
        {
            Log.LogInfo("Accept the player trying to connect to the server");
            NetworkController.Send(recipients, NetworkClient.Port, message);
        }

        public void SendLoadGame(IPAddress[] recipients, LoadGame message)
        {
            Log.LogInfo("Tell the clients to start loading the game");
            NetworkController.Send(recipients, NetworkClient.Port, message);
        }

        public void SendPrepareForPlaying(IPAddress[] recipients)
        {
            Log.LogInfo("All clients have loaded the game. Tell them to prepare playing");
            NetworkController.Send(recipients, NetworkClient.Port, new PrepareForPlaying());
        }

        public void SendInputPressed(IPAddress[] recipients, Guid id, InputState.Key key)
        {
            Log.LogInfo("Sending local input pressed " + key + " to clients");
            NetworkController.Send(recipients, NetworkClient.Port, new InputPressed
                                                                       {
                                                                           Key = key,
                                                                           PlayerId = id.ToString()
                                                                       });
        }

        public void SendInputReleased(IPAddress[] recipients, Guid id, InputState.Key key)
        {
            Log.LogInfo("Sending local input released " + key + " to clients");
            NetworkController.Send(recipients, NetworkClient.Port, new InputReleased
                                                                       {
                                                                           Key = key,
                                                                           PlayerId = id.ToString()
                                                                       });
        }
    }
}